﻿/*

(c) Sense Studios 2008
Sense Studios (http://www.sense-studios.com) all rights reserverd
Part of Canvas, NLtracks
_________     _____    ___________   _________    _________
\_   ___ \   /  _  \   \      \   \ /   /  _  \  /   _____/
/    \  \/  /  /_\  \  /   |   \   Y   /  /_\  \ \_____  \ 
\     \____/    |    \/    |    \     /    |    \/        \
 \______  /\____|__  /\____|__  /\___/\____|__  /_______  /
        \/         \/         \/              \/        \/ 
		
 # The Canvas Class is the collection of ALL interaction functions
 of the canvas, the mousinterface, the background builders and the
 cursors. Also it can set GoTo and CenterOn command.

 
 Usage:
 public functions include, but are not limited to:
	###  GoTo(X, Y)
	###  CenterOn(X, Y)
	
 Also check out Wii-canvas, for Wii controls.
 
 Written by Daan Nolen, Sense studios, refactoring 2010-2011, under MIT License
 
*/

package com.sense.canvas { 

	import flash.display.*
	import flash.events.*
	import flash.text.*
	import flash.geom.*
	import flash.ui.Mouse;
	import flash.net.*
	import flash.geom.Matrix;
	import flash.system.Security;
	import flash.utils.Timer
	
	// import com.sense.utils.BitmapManipulator
	
	public class Canvas extends MovieClip implements iCanvas {

		/*
		snapping is no longer used
		grid can be used for snapping
		for snapping (not used, move to subclass ? )(
		private var doSnapping:String = "false" 
		private var gridX:int
		private var gridY:int
		public var gX:Number = 0
		public var gY:Number = 0
		private var snapping:Boolean = false
		private var snapXisSet = false
		private var snapYisSet = false
		private var snapX:int = 0
		private var snapY:int = 0
		*/
		
		// set size of canvas
		public var sizeX:int = 8000
		public var sizeY:int = 8000
		
		// where the canvas starts
		public var startX:int = 0
		public var startY:int = 0
			
		// physics
		public var fr:Number = 0.8 // friction
		public var eas:Number = 0.4 // easing
		public var bounceFriction:Number = 0.4
		
		// mousewheel speed
		public var mouseWheelMultiplyer:int = 3
		
		// speed on pulse (ie. keypress etc.)
		public var puls:int = 120 // amount of impuls with a keypress
		
		// used to calculate the current window
		private var winX:int = 0
		private var winY:int = 0
		private var winW:int = 0
		private var winH:int = 0

		// internal speed vars
		private var vx:Number = 0
		private var vy:Number = 0
		
		// internal x ad y, used to set the x and y
		private var setX:int = 0
		private var setY:int = 0
		
		// used to calculate friction
		private var oldX:int = 0
		private var oldY:int = 0
		
		// delta distance
		private var dx:int = 0 
		private var dy:int = 0
		
		// used as setter
		private var newX:Number = 0
		private var newY:Number = 0
		
		// checks if the user is currently dragging
		private var isDragging:Boolean = false
	
	
		public function Canvas():void {
			
			// mousewheel
			addEventListener(MouseEvent.MOUSE_DOWN, doMouseDown)
			addEventListener(MouseEvent.MOUSE_WHEEL, doMouseWheel)
			
			// added/removed
			addEventListener(Event.ADDED_TO_STAGE, onAdded)
			addEventListener(Event.REMOVED_FROM_STAGE, doRemove)
		}
		
		private function onAdded(evtObj:Event) {
			
			// this is full browser mode, change this if only a part should be visable
			// also, TODO add mask function if the canvas is running "windowed"
			stage.scaleMode = StageScaleMode.NO_SCALE // 	SHOW_ALL/	NO_BORDER
			stage.align = StageAlign.TOP_LEFT;
			stage.addEventListener(Event.RESIZE, doOnResize)
			
			// key handles go on stage
			stage.addEventListener(KeyboardEvent.KEY_UP, keyUpHandler);
			stage.addEventListener(KeyboardEvent.KEY_DOWN, keyDownHandler);
			
			// start en enterframe listener
			addEventListener(Event.ENTER_FRAME, doMove)
			
			// init canvas with resize
			doOnResize(null)
		}
		
		private function doRemove(e:Event) {
			removeEventListener(MouseEvent.MOUSE_DOWN, doMouseDown)
			removeEventListener(MouseEvent.MOUSE_WHEEL, doMouseWheel)
			removeEventListener(Event.ENTER_FRAME, doMove)
			removeEventListener(Event.ADDED_TO_STAGE, onAdded)
			
			stage.removeEventListener(KeyboardEvent.KEY_UP, keyUpHandler);
			stage.removeEventListener(KeyboardEvent.KEY_DOWN, keyDownHandler);
			stage.removeEventListener(Event.RESIZE, doOnResize)
						
			removeEventListener(MouseEvent.MOUSE_MOVE, trackVelocity)
			stage.removeEventListener(MouseEvent.MOUSE_UP, doMouseUp)
			stage.removeEventListener(Event.MOUSE_LEAVE, doMouseUp)
		}
		
		private function doOnResize(e:Event = null) {
			winW = stage.stageWidth
			winH = stage.stageHeight
			dispatchEvent(new Event(Event.RESIZE))
		}
		
		private function doMouseWheel(evtObj:Event) {
			var mw_move:int = evtObj["delta"] * mouseWheelMultiplyer
			goTo(x, y + mw_move)
		}
		
		private function doMouseDown(e:Event) {
			if (e.target.name == "background") {

				removeEventListener(MouseEvent.MOUSE_DOWN, doMouseDown)
				
				removeEventListener(Event.ENTER_FRAME, doMove)
				addEventListener(Event.ENTER_FRAME, trackVelocity)
				
				stage.addEventListener(MouseEvent.MOUSE_UP, doMouseUp)
				stage.addEventListener(Event.MOUSE_LEAVE, doMouseUp)
				
				dx = 0
				dy = 0
				
				startDrag()
				
				isDragging = true
			}
		}
		
		private function doMouseUp(e:Event) {
			if (isDragging) {

				addEventListener(MouseEvent.MOUSE_DOWN, doMouseDown)
				
				removeEventListener(Event.ENTER_FRAME, trackVelocity)
				addEventListener(Event.ENTER_FRAME, doMove)
				
				stage.removeEventListener(MouseEvent.MOUSE_UP, doMouseUp)
				stage.removeEventListener(Event.MOUSE_LEAVE, doMouseUp)
				
				stopDrag()
				
				isDragging = false
			}
		}
		
		private function trackVelocity(e:Event) { 
			
			setX = x
			setY = y
			
			vx = setX - oldX
			vy = setY - oldY
			
			oldX = setX
			oldY = setY
			
			// Boundries 
			if (setX > winX) {
				dx = 0
				dy = 0
				setX = winX 
			}
			
			if (setY > winY) { 
				dx = 0
				dy = 0
				setY = winY 
			}
			
			if (setX < winW - sizeX) { 
				dx = 0
				dy = 0
				setX = winW - sizeX 
			}
			
			if (setY < winH - sizeY) { 
				dx = 0
				dy = 0
				setY = winH - sizeY 
			}
		}
		
        private function keyUpHandler(event:KeyboardEvent):void {

			switch (event.keyCode) {
				case (38):  // "up"
					goTo(setX, setY+puls)
					break;

				case (37): //"left"
					goTo(setX+puls, setY)
					//trace("left")
					break;
				
				case (39): //"right"
					goTo(setX-puls, setY)
					break;

				case (40): // "down"
					goTo(setX, setY-puls)				
					break;
				
				default:
					break;
			}
        }
		
		private function keyDownHandler(event:KeyboardEvent):void {
			// nothing
		}

		private function doMove(evtObj:Event) {
			
			if (Math.abs(dx) > 5 || Math.abs(dy) > 5) {
				// use Easing
				dx = newX - setX
				dy = newY - setY
				vx = dx * eas
				vy = dy * eas
			
			}else{
				// use Friction
				dx = 0
				dy = 0
				vx *= fr
				vy *= fr
			}
			
			// Roundup
			if (Math.abs(vx) < 0.002) vx = 0
			if (Math.abs(vy) < 0.002) vy = 0
			
			// Bounce 
			if (setX > winX) {
				dx = 0
				dy = 0
				setX = winX
				vx = -vx * bounceFriction
			}
			
			if (setY > winY) { 
				dx = 0
				dy = 0
				setY = winY
				vy = -vy * bounceFriction
			}
			
			if (setX < winW - sizeX) {
				dx = 0
				dy = 0
				setX = winW - sizeX
				vx = -vx * bounceFriction
			}

			if (setY < winH - sizeY) {
				dx = 0
				dy = 0
				setY = winH - sizeY
				vy = -vy * bounceFriction
			}
			
			// Move-Set
			setX += vx
			setY += vy
			
			// Actual Move
			x = setX
			y = setY
		}
		
		
		// ############################
		// ### Move Implementations ###
		// ############################
		
		// Public Moving functions		
		public function goTo(goX:Number, goY:Number):void {
			newX = goX
			newY = goY
			dx = newX - setX
			dy = newY - setY
		}
		
		// Public Moving functions		
		public function centerOn(goX:Number, goY:Number):void {
			newX = goX + (stage.stageWidth * 0.5)
			newY = goY + (stage.stageHeight * 0.5)
			dx = newX - setX
			dy = newY - setY
		}
	}
}

